package com.yayohos.locationmanager.configuration;


import com.yayohos.locationmanager.constants.ProviderType;
import com.yayohos.locationmanager.helper.StringUtils;
import com.yayohos.locationmanager.providers.dialogprovider.DialogProvider;
import com.yayohos.locationmanager.providers.dialogprovider.SimpleMessageDialogProvider;

/**
 * DefaultProviderConfiguration 默认的provider配置类
 */
public final class DefaultProviderConfiguration {
    private final long requiredTimeInterval;
    private final long requiredDistanceInterval;
    private final float acceptableAccuracy;
    private final long acceptableTimePeriod;
    private final long gpsWaitPeriod;
    private final long networkWaitPeriod;
    private final DialogProvider gpsDialogProvider;

    private DefaultProviderConfiguration(Builder builder) {
        this.requiredTimeInterval = builder.requiredTimeInterval;
        this.requiredDistanceInterval = builder.requiredDistanceInterval;
        this.acceptableAccuracy = builder.acceptableAccuracy;
        this.acceptableTimePeriod = builder.acceptableTimePeriod;
        this.gpsWaitPeriod = builder.gpsWaitPeriod;
        this.networkWaitPeriod = builder.networkWaitPeriod;
        this.gpsDialogProvider = builder.gpsDialogProvider;
    }

    /**
     * 创建一个builder
     *
     * @return Builder
     */
    public Builder newBuilder() {
        return new Builder()
                .requiredTimeInterval(requiredTimeInterval)
                .requiredDistanceInterval(requiredDistanceInterval)
                .acceptableAccuracy(acceptableAccuracy)
                .acceptableTimePeriod(acceptableTimePeriod)
                .setWaitPeriod(ProviderType.GPS, gpsWaitPeriod)
                .setWaitPeriod(ProviderType.NETWORK, networkWaitPeriod)
                .gpsDialogProvider(gpsDialogProvider);
    }

    /**
     * 获取时间间隔
     *
     * @return 时间间隔
     */
    public long requiredTimeInterval() {
        return requiredTimeInterval;
    }

    /**
     * 需要的距离精度
     *
     * @return requiredDistanceInterval
     */
    public long requiredDistanceInterval() {
        return requiredDistanceInterval;
    }

    /**
     * 要求的准确度
     *
     * @return acceptableAccuracy
     */
    public float acceptableAccuracy() {
        return acceptableAccuracy;
    }

    /**
     * 要求的时期
     *
     * @return acceptableTimePeriod
     */
    public long acceptableTimePeriod() {
        return acceptableTimePeriod;
    }

    /**
     * 是否获取过GPS
     *
     * @return gpsDialogProvider
     */
    public boolean askForEnableGPS() {
        return gpsDialogProvider != null;
    }

    /**
     * 获取gpsDialog提供者
     *
     * @return DialogProvider gpsDialogProvider
     */
    public DialogProvider gpsDialogProvider() {
        return gpsDialogProvider;
    }

    /**
     * GPS等待时长
     *
     * @return gpsWaitPeriod
     */
    public long gpsWaitPeriod() {
        return gpsWaitPeriod;
    }

    /**
     * Builder
     */
    public static class Builder {
        private long requiredTimeInterval = Defaults.LOCATION_INTERVAL;
        private long requiredDistanceInterval = Defaults.LOCATION_DISTANCE_INTERVAL;
        private float acceptableAccuracy = Defaults.MIN_ACCURACY;
        private long acceptableTimePeriod = Defaults.TIME_PERIOD;
        private long gpsWaitPeriod = Defaults.WAIT_PERIOD;
        private long networkWaitPeriod = Defaults.WAIT_PERIOD;
        private DialogProvider gpsDialogProvider;
        private String gpsMessage = Defaults.EMPTY_STRING;

        /**
         * requiredTimeInterval
         *
         * @param requiredTimeInterval TimeInterval will be used while getting location from default location providers
         *                             It will define in which period updates need to be delivered
         *                             and will be used only when
         *                             {@linkplain LocationConfiguration#keepTracking()} is set to true.
         *                             Default is {@linkplain Defaults#LOCATION_INTERVAL}
         * @return Builder
         * @throws IllegalArgumentException requiredTimeInterval cannot be set to negative value.
         */
        public Builder requiredTimeInterval(long requiredTimeInterval) {
            if (requiredTimeInterval < 0) {
                throw new IllegalArgumentException("requiredTimeInterval cannot be set to negative value.");
            }

            this.requiredTimeInterval = requiredTimeInterval;
            return this;
        }

        /**
         * requiredDistanceInterval
         *
         * @param requiredDistanceInterval DistanceInterval will be used while getting location from default
         *                                 location providers
         *                                 It will define in which distance changes that we should receive an update
         *                                 and will be used only when
         *                                 {@linkplain LocationConfiguration#keepTracking()} is set to true.
         *                                 Default is {@linkplain Defaults#LOCATION_DISTANCE_INTERVAL}
         * @return Builder
         * @throws IllegalArgumentException requiredDistanceInterval cannot be set to negative value.
         */
        public Builder requiredDistanceInterval(long requiredDistanceInterval) {
            if (requiredDistanceInterval < 0) {
                throw new IllegalArgumentException("requiredDistanceInterval cannot be set to negative value.");
            }

            this.requiredDistanceInterval = requiredDistanceInterval;
            return this;
        }

        /**
         * acceptableAccuracy
         *
         * @param acceptableAccuracy Minimum Accuracy that you seek location to be
         *                           Default is {@linkplain Defaults#MIN_ACCURACY}
         * @return Builder
         * @throws IllegalArgumentException acceptableAccuracy cannot be set to negative value.
         */
        public Builder acceptableAccuracy(float acceptableAccuracy) {
            if (acceptableAccuracy < 0) {
                throw new IllegalArgumentException("acceptableAccuracy cannot be set to negative value.");
            }

            this.acceptableAccuracy = acceptableAccuracy;
            return this;
        }

        /**
         * acceptableTimePeriod
         *
         * @param acceptableTimePeriod Indicates time period that can be count as usable location,
         *                             this needs to be considered such as "last 5 minutes"
         *                             Default is {@linkplain Defaults#TIME_PERIOD}
         * @return Builder
         * @throws IllegalArgumentException acceptableTimePeriod cannot be set to negative value.
         */
        public Builder acceptableTimePeriod(long acceptableTimePeriod) {
            if (acceptableTimePeriod < 0) {
                throw new IllegalArgumentException("acceptableTimePeriod cannot be set to negative value.");
            }

            this.acceptableTimePeriod = acceptableTimePeriod;
            return this;
        }

        /**
         * gpsMessage
         *
         * @param gpsMessage Indicates what to display to user while asking to turn GPS on.
         *                   If you do not set this, user will not be asked to enable GPS.
         * @return Builder
         */
        public Builder gpsMessage(String gpsMessage) {
            this.gpsMessage = gpsMessage;
            return this;
        }

        /**
         * 获取 gpsDialogProvider
         *
         * @param dialogProvider If you need to display a custom dialog to ask user to enable GPS, you can provide
         *                       your own
         *                       implementation of {@linkplain DialogProvider} and manager will use that implementation
         *                       to display the dialog.
         *                       Important, if you set your own implementation, please make sure to handle gpsMessage
         *                       as well.
         *                       Because {@linkplain Builder#gpsMessage} will be ignored in that case.
         *                       <p>
         *                       If you don't specify any dialogProvider implementation
         *                       {@linkplain SimpleMessageDialogProvider} will be used with
         *                       given {@linkplain Builder#gpsMessage}
         * @return Builder
         */
        public Builder gpsDialogProvider(DialogProvider dialogProvider) {
            this.gpsDialogProvider = dialogProvider;
            return this;
        }

        /**
         * 设置等待时间
         *
         * @param milliseconds float
         * @param providerType Indicates waiting time period before switching to next possible provider.
         *                     Possible to set provider wait periods separately by passing providerType as one of the
         *                     {@linkplain ProviderType} values.
         *                     Default values are {@linkplain Defaults#WAIT_PERIOD}
         * @return Builder
         * @throws IllegalArgumentException waitPeriod cannot be set to negative value.
         */
        public Builder setWaitPeriod(int providerType, long milliseconds) {
            if (milliseconds < 0) {
                throw new IllegalArgumentException("waitPeriod cannot be set to negative value.");
            }
            switch (providerType) {
                case ProviderType.NETWORK:
                    this.networkWaitPeriod = milliseconds;
                    break;
                case ProviderType.GPS:
                    this.gpsWaitPeriod = milliseconds;
                    break;
                case ProviderType.DEFAULT_PROVIDERS:
                    this.gpsWaitPeriod = milliseconds;
                    break;
                case ProviderType.NONE:
                    // ignored
                    break;
                default:
                    break;
            }
            return this;
        }

        /**
         * 构建DefaultProviderConfiguration实例
         *
         * @return DefaultProviderConfiguration
         */
        public DefaultProviderConfiguration build() {
            if (gpsDialogProvider == null && StringUtils.isNotEmpty(gpsMessage)) {
                gpsDialogProvider = new SimpleMessageDialogProvider(gpsMessage);
            }
            return new DefaultProviderConfiguration(this);
        }
    }
}
